home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-04 / bayes.zip / BAYES.AUG < prev   
Text File  |  1991-03-11  |  13KB  |  502 lines

  1. program BayesNet(NodeFile,LinkFile,Output);
  2. {
  3.    This code is a basic implementation of Judea Pearl's belief
  4.    propagation algorithm for tree-structured Bayesian belief
  5.    networks.  The procedures and functions can be divided into
  6.    three basic groups:
  7.    
  8.    Math Support:
  9.       Normalize
  10.       MakeIdentityVector
  11.       TermProduct
  12.       TermQuotient
  13.       MatMult
  14.    Core:
  15.       ReviseBelief
  16.       UpdateNode
  17.       SubmitEvidence
  18.    General Support:
  19.       ReadString
  20.       FindNode
  21.       DumpNetwork
  22.          DumpNode
  23.       ReadNet
  24.          ReadNodes
  25.          ReadLinks
  26.       
  27.    The Core routines are described in the August AI Expert article.
  28.    The main program is set up to run the example from the May AI
  29.    Expert article.  It reads the net from two data files which are
  30.    described in ReadNodes and ReadLinks.  Be sure to figure out how
  31.    to RESET these files so that they get picked up correctly by those
  32.    procedures.   
  33. }
  34.  
  35. const
  36.    MaxString      = 15;
  37.    MaxValues      =  5;
  38.          
  39. type
  40.    StringRange = 1..MaxString;
  41.    ValueRange  = 1..MaxValues;
  42.    StringType  = packed array[StringRange] of char;
  43.    NetVector   = record
  44.                     Data:  array [ValueRange] of real;
  45.                     NVals: ValueRange
  46.                  end;
  47.    CPType      = record
  48.                     Data:        array[ValueRange,ValueRange] of real;
  49.                     NRows,NCols: ValueRange
  50.                  end;
  51.    NetNodePtr  = ^NetNode;
  52.    NetNode     = record
  53.                     Name:                       StringType;
  54.                     NumValues:                  ValueRange;
  55.                     Values:                     array[ValueRange] of 
  56. StringType;
  57.                     Belief,Pi,IncomingPi,
  58.                     ExternalLambda,
  59.                     Lambda,OutgoingLambda:      NetVector;
  60.                     Parent,NextNode,
  61.                     NextSibling,FirstChild:     NetNodePtr;
  62.                     CPMatrix,TransCPMatrix:     CPType
  63.                  end;
  64.                         
  65. var      NodeFile,LinkFile:            Text;
  66.          NetRoot,NodeList:             NetNodePtr;
  67.          EvidenceVector:               NetVector;
  68.  
  69. { ******************** Math Support ******************** }
  70.         
  71. procedure Normalize(var Vector: NetVector);
  72. { Scales incoming Vector so that it sums to unity }
  73. var i:   ValueRange;
  74.     Sum: real;
  75.  
  76. begin
  77. Sum := 0;
  78. with Vector do
  79.    begin
  80.    for i := 1 to NVals do
  81.       Sum := Sum + Data[i];
  82.    for i := 1 to NVals do
  83.       Data[i] := Data[i] / Sum
  84.    end
  85. end;
  86.          
  87. procedure MakeIdentityVector(var Vector: NetVector;Length: ValueRange);
  88. { Makes incoming Vector into an identity vector of specified length}
  89. var i: ValueRange;
  90.  
  91. begin
  92. with Vector do
  93.    begin   
  94.    NVals := Length;
  95.    for i := 1 to Length do
  96.       Data[i] := 1.0   
  97.    end   
  98. end;
  99.  
  100. procedure TermProduct(var V1,V2,Result: NetVector);
  101. { Returns term product of V1 and V2 in Result }                      
  102. var i:  ValueRange;
  103.  
  104. begin
  105. if v1.NVals <> v2.Nvals then
  106.    writeln('*** Dimension error in TermProduct ***');
  107. with Result do
  108.    begin
  109.    Nvals := V1.Nvals;
  110.    for i := 1 to NVals do
  111.       Data[i] := V1.Data[i] * V2.Data[i]
  112.    end
  113.  
  114. end;
  115.  
  116. procedure TermQuotient(var V1,V2,Result: NetVector);
  117. { Returns term quotient of V1 and V2 in Result }                               
  118.              
  119. var i:  ValueRange;
  120.  
  121. begin
  122. if v1.NVals <> v2.Nvals then
  123.    writeln('*** Dimension error in TermQuotient ***');
  124. with Result do
  125.    begin
  126.    Nvals := V1.Nvals;
  127.    for i := 1 to NVals do
  128.       Data[i] := V1.Data[i] / V2.Data[i]
  129.    end
  130. end;
  131.  
  132. procedure MatMult(var InMat:  CPType;var InVec:  NetVector;var OutVec: 
  133. NetVector);
  134. { Simplified matrix multiplication matrix routine.  Multiplies InMat * InVec
  135.   to produce OutVec.  Interprets InVec to be a NVals X 1 matrix. }
  136. var Row,Col: ValueRange;
  137.  
  138. begin
  139. if InMat.NCols <> InVec.NVals then
  140.    writeln('*** Dimension error in MatMult ***');
  141. with InMat do
  142.    begin
  143.    OutVec.NVals := NRows;
  144.    for Row := 1 to NRows do
  145.       begin
  146.       OutVec.Data[Row] := 0.0;
  147.       for Col := 1 to NCols do
  148.          OutVec.Data[Row] := OutVec.Data[Row] + Data[Row,Col] * InVec.Data[Col]
  149.        end
  150.    end
  151. end;
  152.  
  153. { ******************** Core ******************** }
  154.  
  155. procedure ReviseBelief(Node: NetNodePtr);
  156. var Child:  NetNodePtr;
  157. begin
  158. with Node^ do
  159.    begin
  160.    { Part (a) of Figure 4 }
  161.    if Parent <> nil then
  162.       MatMult(TransCPMatrix,IncomingPi,Pi);
  163.    { Part (b) of Figure 4 }
  164.    Lambda := ExternalLambda;
  165.    Child := FirstChild;
  166.    while Child <> nil do
  167.       begin
  168.       TermProduct(Child^.OutgoingLambda,Lambda,Lambda);
  169.       Child := Child^.NextSibling
  170.       end;
  171.    { Shaded part of Figure 4 }
  172.    TermProduct(Lambda,Pi,Belief);   
  173.    Normalize(Belief)
  174.    end
  175. end;
  176.  
  177. procedure UpdateNode(Node,Sender: NetNodePtr);
  178. var Child:  NetNodePtr;
  179. begin
  180. with Node^ do
  181.    begin
  182.    ReviseBelief(Node);
  183.    { Update OutgoingLambda & send update message to parent
  184.      (part (c) of Figure 4) }
  185.    if (Parent <> Sender) and (Parent <> nil) then
  186.       begin
  187.       MatMult(CPMatrix,Lambda,OutgoingLambda);
  188.       UpdateNode(Parent,Node)
  189.       end;
  190.    { Update IncomingPi and send update message to children
  191.      (part (d) of Figure 4) }
  192.    Child := FirstChild;
  193.    while Child <> nil do
  194.       begin
  195.       if Child <> Sender then
  196.          begin
  197.          TermQuotient(Belief,Child^.OutgoingLambda,Child^.IncomingPi);
  198.          UpdateNode(Child,Node)
  199.          end;
  200.       Child := Child^.NextSibling
  201.       end
  202.    end
  203. end;
  204.  
  205. procedure SubmitEvidence(Node: NetNodePtr;var Evidence: NetVector);
  206. var i: ValueRange;
  207. begin
  208. with node^ do
  209.    begin
  210.    writeln('Submitting evidence to ',Node^.Name,', evidence is:');
  211.    for i := 1 to Evidence.NVals do
  212.       writeln('[',Values[i],'] = ',Evidence.Data[i]);
  213.    TermProduct(Evidence,ExternalLambda,ExternalLambda);
  214.    UpdateNode(Node,nil)
  215.    end
  216. end;
  217.  
  218. { ******************** General Support ******************** }
  219.  
  220. function ReadString(var InFile: Text;var InString: StringType): boolean;
  221. { Reads InFile, returning next string in InString.  Returns FALSE upon
  222.   encountering end of file, otherwise returns TRUE. }
  223. var i,j:  StringRange;
  224.  
  225. begin
  226. if eof(InFile) then
  227.    ReadString := false
  228. else
  229.    begin
  230.    i := 1;
  231.    while not eoln(InFile) do
  232.       begin
  233.       read(InFile,InString[i]);
  234.       i := i + 1
  235.       end;
  236.    readln(InFile);
  237.    for j := i to MaxString do
  238.       InString[j] := ' ';
  239.    ReadString := true   
  240.    end;   
  241. end;
  242.          
  243. function FindNode(NodeName: StringType):NetNodePtr;
  244. { Searches network for node having specified NodeName. }                  
  245. var CurrentNode:   NetNodePtr;
  246.  
  247. begin
  248. CurrentNode := NodeList;
  249. while (CurrentNode^.Name <> NodeName) and (CurrentNode <> nil) do
  250.    CurrentNode := CurrentNode^.NextNode;
  251. if CurrentNode = nil then
  252.    begin
  253.    writeln('*** Error in FindNode -- cannot find ',NodeName);
  254.    FindNode := nil
  255.    end
  256. else
  257.    FindNode := CurrentNode
  258. end;
  259.         
  260. procedure DumpNetwork(Node: NetNodePtr);
  261. { Recursive procedure to dump network, given pointer to root }
  262.  
  263. procedure DumpNode(Node: NetNodePtr);
  264. { Simple procedure to dump a single node }
  265. const Stars = '*************************************************';
  266.  
  267. var CurrentValue,NumRows,NumCols,Row,Col:  ValueRange;
  268.  
  269. begin
  270. writeln(Stars);
  271. with Node^ do
  272.    begin
  273.    writeln('Dumping ',Name);
  274.    for CurrentValue := 1 to NumValues do
  275.       writeln('Pi[',Values[CurrentValue],'] = ',Pi.Data[CurrentValue]);
  276.    for CurrentValue := 1 to NumValues do
  277.       writeln('Lambda[',Values[CurrentValue],'] = ',Lambda.Data[CurrentValue]);
  278.    for CurrentValue := 1 to NumValues do
  279.       writeln('Belief[',Values[CurrentValue],'] = ',Belief.Data[CurrentValue]);
  280.    if Parent <> nil then
  281.       begin
  282.       writeln;
  283.       writeln('CP Matrix:');
  284.       for Row := 1 to CPMatrix.NRows do
  285.          begin
  286.          for Col := 1 to CPMatrix.NCols do
  287.             write(CPMatrix.Data[Row,Col]);
  288.          writeln
  289.          end
  290.       end
  291.    end;
  292. writeln(Stars);
  293. writeln('Type <cr> to continue...');
  294. readln
  295. end;   { of DumpNode }
  296.  
  297. var CurrentNode: NetNodePtr;
  298.  
  299. begin
  300. if Node <> nil then
  301.    begin
  302.    DumpNode(Node);
  303.    CurrentNode := Node^.FirstChild;
  304.    while CurrentNode <> nil do
  305.       begin
  306.       DumpNetwork(CurrentNode);
  307.       CurrentNode := CurrentNode^.NextSibling
  308.       end 
  309.    end  
  310. end;
  311.  
  312. procedure ReadNet(var NodeFile,LinkFile: Text);
  313.        
  314. procedure ReadNodes(Var NodeFile: Text);
  315. { This procedure reads the NodeFile.  Format of file is as follows:
  316.  
  317.    Node 1 name
  318.    Node 1 number of values
  319.    Node 1 value 1 name
  320.    Node 1 value 1 prior probability (ignored except for root node)
  321.    Node 1 value 2 name
  322.    Node 1 value 2 prior probability (ignored except for root node)
  323.            .....
  324.    Node 1 value n name
  325.    Node 1 value n prior probability (ignored except for root node)
  326.    Node 2 name
  327.            .....
  328.            etc.
  329. }   
  330. var NodeName:      StringType;
  331.     CurrentValue:  ValueRange;
  332.     eofStatus:     boolean;
  333.     CurrentNode:   NetNodePtr;
  334.                     
  335. begin
  336. reset(NodeFile);
  337. NodeList := nil;
  338. while ReadString(NodeFile,NodeName) do
  339.    begin
  340.    new(CurrentNode);
  341.    with CurrentNode^ do
  342.       begin
  343.       Name := NodeName;
  344.       readln(NodeFile,NumValues);
  345.       for CurrentValue := 1 to NumValues do
  346.          begin
  347.          eofStatus := ReadString(NodeFile,Values[CurrentValue]);
  348.          readln(NodeFile,Pi.Data[CurrentValue])
  349.          end;
  350.       Pi.NVals := NumValues;
  351.       Parent := nil;
  352.       NextSibling := nil;
  353.       FirstChild := nil;
  354.       NextNode := NodeList;
  355.       NodeList := CurrentNode;
  356.       MakeIdentityVector(ExternalLambda,NumValues);
  357.       MakeIdentityVector(Lambda,NumValues)
  358.       end
  359.    end;
  360. close(NodeFile)
  361. end;   { or ReadNodes }
  362.  
  363. procedure ReadLinks(var LinkFile: Text);
  364. { This procedure reads the NodeFile.  Be careful here, upper/lower case
  365.   must match identically the node names in NodeFile.  Format of file is
  366.   as follows:
  367.  
  368.    Top Node name for first link
  369.    Bottom Node name for first link
  370.    1st row of CP matrix
  371.    2nd row of CP matrix
  372.           ....
  373.    nth row of CP matrix
  374.    Top Node name for second link
  375.    Bottom Node name for second link
  376.    1st row of CP matrix
  377.    2nd row of CP matrix
  378.           ....
  379.    nth row of CP matrix
  380.        etc.
  381. }
  382. var TopNodeName,BottomNodeName:      StringType;
  383.     TopNode,BottomNode:              NetNodePtr;
  384.     Row,Col:                         ValueRange;
  385.     eofStatus:                       boolean;
  386.                     
  387. begin
  388. reset(LinkFile);
  389. while ReadString(LinkFile,TopNodeName) do
  390.    begin
  391.    TopNode := FindNode(TopNodeName);
  392.    eofStatus := ReadString(LinkFile,BottomNodeName);
  393.    BottomNode := FindNode(BottomNodeName);
  394.    with BottomNode^ do
  395.       begin
  396.       CPMatrix.NRows := TopNode^.NumValues;
  397.       CPMatrix.NCols := NumValues;
  398.       TransCPMatrix.NRows := CPMatrix.Ncols;
  399.       TransCPMatrix.NCols := CPMatrix.NRows;
  400.       for Row := 1 to CPMatrix.NRows do
  401.          begin
  402.          for Col := 1 to CPMatrix.Ncols do
  403.             begin
  404.             read(LinkFile,CPMatrix.Data[Row,Col]);
  405.             TransCPMatrix.Data[Col,Row] := CPMatrix.Data[Row,Col]
  406.             end;
  407.          readln(LinkFile)
  408.          end;
  409.       NextSibling := TopNode^.FirstChild;
  410.       Parent := TopNode;
  411.       MakeIdentityVector(OutgoingLambda,TopNode^.NumValues)
  412.       end;
  413.    TopNode^.FirstChild := BottomNode
  414.    end
  415. end;   { of ReadLinks }
  416.  
  417. begin
  418. ReadNodes(NodeFile);
  419. ReadLinks(LinkFile);
  420. { Find root of network. }
  421. NetRoot := NodeList;
  422. while NetRoot^.Parent <> nil do
  423.    NetRoot := NetRoot^.NextNode;
  424. { Initialize network }   
  425. UpdateNode(NetRoot,nil)
  426. end;
  427.      
  428. begin
  429.  
  430. { Read network in }
  431. ReadNet(NodeFile,LinkFile);
  432.  
  433. { Take a look }
  434. DumpNetwork(NetRoot);
  435.  
  436. { Store evidence from rain alarm in EvidenceVector }
  437. with EvidenceVector do
  438.    begin
  439.    Data[1] := 0.8;
  440.    Data[2] := 0.04;
  441.    NVals := 2
  442.    end;
  443.  
  444. { Submit EvidenceVector to Rain node }   
  445. SubmitEvidence(FindNode('Rain           '),EvidenceVector);
  446.  
  447. { Take a look }
  448. DumpNetwork(NetRoot);
  449.  
  450. { Store evidence from telephone call in EvidenceVector }
  451. with EvidenceVector do
  452.    begin
  453.    Data[1] := 1.0;
  454.    Data[2] := 0.02;
  455.    NVals := 2
  456.    end;
  457.  
  458. { Submit EvidenceVector to Sunburn node }   
  459. SubmitEvidence(FindNode('Sunburn        '),EvidenceVector);
  460.  
  461. { Take a look }
  462. DumpNetwork(NetRoot)
  463.  
  464. end.
  465.  
  466. Clouds
  467. Rain
  468. 0.6 0.4
  469. 0.0 1.0
  470. Rain
  471. Play Game
  472. 0.05 0.95
  473. 1.00 0.00
  474. Clouds
  475. Sunburn
  476. 0.1 0.9
  477. 0.7 0.3
  478. Clouds
  479. 2
  480. Present
  481. .1
  482. Absent
  483. .9
  484. Rain
  485. 2
  486. Present
  487. 0
  488. Absent
  489. 0
  490. Play Game
  491. 2
  492. Yes
  493. 0
  494. No
  495. 0
  496. Sunburn
  497. 2
  498. Yes
  499. 0
  500. No
  501. 0
  502.